/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2018 Live Networks, Inc.  All rights reserved.
// MPEG4-GENERIC ("audio", "video", or "application") RTP stream sources
// Implementation

#include "include/MPEG4GenericRTPSource.hh"
#include "include/BitVector.hh"
#include "include/MPEG4LATMAudioRTPSource.hh" // for parseGeneralConfigStr()

////////// MPEG4GenericBufferedPacket and MPEG4GenericBufferedPacketFactory

class MPEG4GenericBufferedPacket : public BufferedPacket {
public:
    MPEG4GenericBufferedPacket(MPEG4GenericRTPSource *ourSource);

    virtual ~MPEG4GenericBufferedPacket();

private: // redefined virtual functions
    virtual unsigned nextEnclosedFrameSize(unsigned char *&framePtr,
                                           unsigned dataSize);

private:
    MPEG4GenericRTPSource *fOurSource;
};

class MPEG4GenericBufferedPacketFactory : public BufferedPacketFactory {
private: // redefined virtual functions
    virtual BufferedPacket *createNewPacket(MultiFramedRTPSource *ourSource);
};


////////// AUHeader //////////
struct AUHeader {
    unsigned size;
    unsigned index; // indexDelta for the 2nd & subsequent headers
};


///////// MPEG4GenericRTPSource implementation ////////

//##### NOTE: INCOMPLETE!!! Support more modes, and interleaving #####

MPEG4GenericRTPSource *
MPEG4GenericRTPSource::createNew(UsageEnvironment &env, Groupsock *RTPgs,
                                 unsigned char rtpPayloadFormat,
                                 unsigned rtpTimestampFrequency,
                                 char const *mediumName,
                                 char const *mode,
                                 unsigned sizeLength, unsigned indexLength,
                                 unsigned indexDeltaLength
) {
    return new MPEG4GenericRTPSource(env, RTPgs, rtpPayloadFormat,
                                     rtpTimestampFrequency, mediumName,
                                     mode, sizeLength, indexLength,
                                     indexDeltaLength
    );
}

MPEG4GenericRTPSource
::MPEG4GenericRTPSource(UsageEnvironment &env, Groupsock *RTPgs,
                        unsigned char rtpPayloadFormat,
                        unsigned rtpTimestampFrequency,
                        char const *mediumName,
                        char const *mode,
                        unsigned sizeLength, unsigned indexLength,
                        unsigned indexDeltaLength
)
        : MultiFramedRTPSource(env, RTPgs,
                               rtpPayloadFormat, rtpTimestampFrequency,
                               new MPEG4GenericBufferedPacketFactory),
          fSizeLength(sizeLength), fIndexLength(indexLength),
          fIndexDeltaLength(indexDeltaLength),
          fNumAUHeaders(0), fNextAUHeader(0), fAUHeaders(NULL) {
    unsigned mimeTypeLength =
            strlen(mediumName) + 14 /* strlen("/MPEG4-GENERIC") */ + 1;
    fMIMEType = new char[mimeTypeLength];
    if (fMIMEType != NULL) {
        sprintf(fMIMEType, "%s/MPEG4-GENERIC", mediumName);
    }

    fMode = strDup(mode);
    // Check for a "mode" that we don't yet support: //#####
    if (mode == NULL ||
        (strcmp(mode, "aac-hbr") != 0 && strcmp(mode, "generic") != 0)) {
        envir() << "MPEG4GenericRTPSource Warning: Unknown or unsupported \"mode\": "
                << mode << "\n";
    }
}

MPEG4GenericRTPSource::~MPEG4GenericRTPSource() {
    delete[] fAUHeaders;
    delete[] fMode;
    delete[] fMIMEType;
}

Boolean MPEG4GenericRTPSource
::processSpecialHeader(BufferedPacket *packet,
                       unsigned &resultSpecialHeaderSize) {
    unsigned char *headerStart = packet->data();
    unsigned packetSize = packet->dataSize();

    fCurrentPacketBeginsFrame = fCurrentPacketCompletesFrame;
    // whether the *previous* packet ended a frame

    // The RTP "M" (marker) bit indicates the last fragment of a frame:
    fCurrentPacketCompletesFrame = packet->rtpMarkerBit();

    // default values:
    resultSpecialHeaderSize = 0;
    fNumAUHeaders = 0;
    fNextAUHeader = 0;
    delete[] fAUHeaders;
    fAUHeaders = NULL;

    if (fSizeLength > 0) {
        // The packet begins with a "AU Header Section".  Parse it, to
        // determine the "AU-header"s for each frame present in this packet:
        resultSpecialHeaderSize += 2;
        if (packetSize < resultSpecialHeaderSize) return False;

        unsigned AU_headers_length = (headerStart[0] << 8) | headerStart[1];
        unsigned AU_headers_length_bytes = (AU_headers_length + 7) / 8;
        if (packetSize
            < resultSpecialHeaderSize + AU_headers_length_bytes)
            return False;
        resultSpecialHeaderSize += AU_headers_length_bytes;

        // Figure out how many AU-headers are present in the packet:
        int bitsAvail = AU_headers_length - (fSizeLength + fIndexLength);
        if (bitsAvail >= 0 && (fSizeLength + fIndexDeltaLength) > 0) {
            fNumAUHeaders = 1 + bitsAvail / (fSizeLength + fIndexDeltaLength);
        }
        if (fNumAUHeaders > 0) {
            fAUHeaders = new AUHeader[fNumAUHeaders];
            // Fill in each header:
            BitVector bv(&headerStart[2], 0, AU_headers_length);
            fAUHeaders[0].size = bv.getBits(fSizeLength);
            fAUHeaders[0].index = bv.getBits(fIndexLength);

            for (unsigned i = 1; i < fNumAUHeaders; ++i) {
                fAUHeaders[i].size = bv.getBits(fSizeLength);
                fAUHeaders[i].index = bv.getBits(fIndexDeltaLength);
            }
        }

    }

    return True;
}

char const *MPEG4GenericRTPSource::MIMEtype() const {
    return fMIMEType;
}


////////// MPEG4GenericBufferedPacket
////////// and MPEG4GenericBufferedPacketFactory implementation

MPEG4GenericBufferedPacket
::MPEG4GenericBufferedPacket(MPEG4GenericRTPSource *ourSource)
        : fOurSource(ourSource) {
}

MPEG4GenericBufferedPacket::~MPEG4GenericBufferedPacket() {
}

unsigned MPEG4GenericBufferedPacket
::nextEnclosedFrameSize(unsigned char *& /*framePtr*/, unsigned dataSize) {
    // WE CURRENTLY DON'T IMPLEMENT INTERLEAVING.  FIX THIS! #####
    AUHeader *auHeader = fOurSource->fAUHeaders;
    if (auHeader == NULL) return dataSize;
    unsigned numAUHeaders = fOurSource->fNumAUHeaders;

    if (fOurSource->fNextAUHeader >= numAUHeaders) {
        fOurSource->envir() << "MPEG4GenericBufferedPacket::nextEnclosedFrameSize("
                            << dataSize << "): data error ("
                            << auHeader << "," << fOurSource->fNextAUHeader
                            << "," << numAUHeaders << ")!\n";
        return dataSize;
    }

    auHeader = &auHeader[fOurSource->fNextAUHeader++];
    return auHeader->size <= dataSize ? auHeader->size : dataSize;
}

BufferedPacket *MPEG4GenericBufferedPacketFactory
::createNewPacket(MultiFramedRTPSource *ourSource) {
    return new MPEG4GenericBufferedPacket((MPEG4GenericRTPSource *) ourSource);
}


////////// samplingFrequencyFromAudioSpecificConfig() implementation //////////

static unsigned const samplingFrequencyFromIndex[16] = {
        96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
        16000, 12000, 11025, 8000, 7350, 0, 0, 0
};

unsigned samplingFrequencyFromAudioSpecificConfig(char const *configStr) {
    unsigned char *config = NULL;
    unsigned result = 0; // if returned, indicates an error

    do {
        // Begin by parsing the config string:
        unsigned configSize;
        config = parseGeneralConfigStr(configStr, configSize);
        if (config == NULL) break;

        if (configSize < 2) break;
        unsigned char samplingFrequencyIndex = ((config[0] & 0x07) << 1) | (config[1] >> 7);
        if (samplingFrequencyIndex < 15) {
            result = samplingFrequencyFromIndex[samplingFrequencyIndex];
            break;
        }

        // Index == 15 means that the actual frequency is next (24 bits):
        if (configSize < 5) break;
        result =
                ((config[1] & 0x7F) << 17) | (config[2] << 9) | (config[3] << 1) | (config[4] >> 7);
    } while (0);

    delete[] config;
    return result;
}
